/*
 * echo_client.cpp
 * 
 * Echo 服务器测试客户端
 * 使用 Protobuf 协议
 */

#include "protocol/PacketHeader.h"
#include "protocol/MessageCodec.h"
#include "echo.pb.h"  // 生成的 protobuf 文件在 build 目录

#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>
#include <muduo/base/Timestamp.h>

#include <iostream>
#include <string>

using namespace muduo;
using namespace muduo::net;
using namespace protocol;

class EchoClient {
public:
    EchoClient(EventLoop* loop, const InetAddress& serverAddr)
        : client_(loop, serverAddr, "EchoClient"),
          loop_(loop) {
        // 设置连接回调
        client_.setConnectionCallback(
            std::bind(&EchoClient::onConnection, this, _1));
        
        // 设置消息回调
        client_.setMessageCallback(
            std::bind(&EchoClient::onMessage, this, _1, _2, _3));
        
        client_.enableRetry();
    }

    void connect() {
        client_.connect();
    }
    
    void disconnect() {
        client_.disconnect();
    }

private:
    void onConnection(const TcpConnectionPtr& conn) {
        LOG_INFO << conn->localAddress().toIpPort() << " -> "
                 << conn->peerAddress().toIpPort() << " is "
                 << (conn->connected() ? "UP" : "DOWN");
        
        if (conn->connected()) {
            // 连接成功，发送测试消息
            sendEchoRequest(conn, "Hello Protobuf Echo Server!");
            sendEchoRequest(conn, "This is message 2");
            sendEchoRequest(conn, "测试中文消息");
        }
    }

    void onMessage(const TcpConnectionPtr& conn,
                   Buffer* buf,
                   Timestamp time) {
        // 解码消息
        while (buf->readableBytes() >= PacketHeader::kHeaderSize) {
            uint16_t msgId;
            std::string data;
            int ret = MessageCodec::decode(buf, msgId, data);
            
            if (ret > 0) {
                // 成功解码
                handleMessage(conn, msgId, data, time);
            } else if (ret == 0) {
                // 包不完整
                break;
            } else {
                // 错误
                LOG_ERROR << "Decode error";
                conn->shutdown();
                break;
            }
        }
    }
    
    void handleMessage(const TcpConnectionPtr& conn,
                      uint16_t msgId,
                      const std::string& data,
                      Timestamp time) {
        if (msgId == MSG_ECHO_RESPONSE) {
            // 解析 Echo 响应
            EchoResponse response;
            if (response.ParseFromString(data)) {
                LOG_INFO << "Received echo response: " << response.message();
                LOG_INFO << "Server timestamp: " << response.timestamp();
                
                // 收到3条消息后断开连接
                static int count = 0;
                if (++count >= 3) {
                    LOG_INFO << "All messages received, disconnecting...";
                    loop_->runAfter(1.0, [conn]() {
                        conn->shutdown();
                    });
                }
            } else {
                LOG_ERROR << "Failed to parse EchoResponse";
            }
        } else {
            LOG_WARN << "Unknown message ID: " << msgId;
        }
    }
    
    void sendEchoRequest(const TcpConnectionPtr& conn, const std::string& msg) {
        // 构造请求
        EchoRequest request;
        request.set_message(msg);
        
        // 编码并发送
        Buffer buf;
        MessageCodec::encode(MSG_ECHO_REQUEST, request, &buf);
        conn->send(&buf);
        
        LOG_INFO << "Sent echo request: " << msg;
    }

    TcpClient client_;
    EventLoop* loop_;
};

int main(int argc, char* argv[]) {
    LOG_INFO << "pid = " << getpid();
    
    // 服务器地址
    std::string serverIp = "127.0.0.1";
    uint16_t serverPort = 8080;
    
    if (argc > 1) {
        serverIp = argv[1];
    }
    if (argc > 2) {
        serverPort = static_cast<uint16_t>(atoi(argv[2]));
    }
    
    LOG_INFO << "Connecting to " << serverIp << ":" << serverPort;
    
    EventLoop loop;
    InetAddress serverAddr(serverIp, serverPort);
    EchoClient client(&loop, serverAddr);
    
    client.connect();
    loop.loop();
    
    return 0;
}

